#include "caller_manager.h"
#include <QMetaType>
#include <algorithm>

namespace application {
namespace caller {

CallerManager::CallerManager(std::shared_ptr<kernel::ILogger> logger, QObject* parent)
    : QObject(parent)
    , m_logger(logger)
{
    qRegisterMetaType<CallerMessage>("CallerMessage");
}

void CallerManager::initConnection(quint16 port)
{
    m_tcpSocketServer = new infrastructure::io::TcpSocketServer(20, this);

    if (!m_tcpSocketServer->listen(QHostAddress::Any, port)) {
        if (m_logger) {
            m_logger->error(QString("呼叫器服务器监听失败: %1").arg(m_tcpSocketServer->errorString()));
        }
        return;
    }

    if (m_logger) {
        m_logger->info(QString("呼叫器服务器启动: 端口=%1").arg(port));
    }

    connect(m_tcpSocketServer, &infrastructure::io::TcpSocketServer::sig_connectClient,
            this, &CallerManager::slot_incomingNewConnection);
}

void CallerManager::createTaskConnection(int callerNum, const QString& taskName)
{
    if (!m_callerNumToTaskName.contains(callerNum)) {
        m_callerNumToTaskName.insert(callerNum, taskName);
        if (m_logger) {
            m_logger->info(QString("创建呼叫器映射: 编号=%1, 任务=%2").arg(callerNum).arg(taskName));
        }
    } else if (m_logger) {
        m_logger->warning(QString("呼叫器编号 %1 已存在映射").arg(callerNum));
    }

    auto& info = ensureRuntimeInfo(callerNum);
    info.callerNum = callerNum;
    info.taskName = taskName;
    notifyRuntimeChanged();
}

void CallerManager::removeTaskConnection(int callerNum)
{
    if (m_callerNumToTaskName.remove(callerNum) > 0) {
        if (m_logger) {
            m_logger->info(QString("移除呼叫器映射: 编号=%1").arg(callerNum));
        }
    }

    if (m_runtimeInfos.remove(callerNum) > 0) {
        notifyRuntimeChanged();
    }
}

void CallerManager::slot_incomingNewConnection(qint64 socketDescriptor, const QString& ip, quint16 port)
{
    auto clients = m_tcpSocketServer->getClients();
    if (!clients.contains(socketDescriptor)) {
        if (m_logger) {
            m_logger->error(QString("无法找到客户端连接: %1").arg(socketDescriptor));
        }
        return;
    }

    infrastructure::io::TcpSocketClient* tmpTcp = clients[socketDescriptor];
    CallerProtocol* protocol = new CallerProtocol(tmpTcp, m_logger, this);
    m_clientToProtocol.insert(tmpTcp, protocol);
    connect(protocol, &CallerProtocol::destroyed, this, [this, protocol]() {
        m_protocolToCaller.remove(protocol);
    });

    if (m_logger) {
        m_logger->info(QString("呼叫器上线: IP=%1, 端口=%2").arg(ip).arg(port));
    }

    connect(tmpTcp, &infrastructure::io::TcpSocketClient::disconnected,
            this, &CallerManager::slot_disconnected);

    protocol->setAnalyzeCallbackFunc([this, protocol](QByteArray& receiveData) {
        if (receiveData.size() < 3) return;

        uint8_t cmdType = receiveData.at(2);
        switch (cmdType) {
            case 0x50:
                responseLogin(protocol);
                break;
            case 0x51:
                responseAuthentication(receiveData, protocol);
                break;
            case 0x70:
                responseButtonPressed(receiveData, protocol);
                break;
            default:
                if (m_logger) {
                    m_logger->warning(QString("未知呼叫器命令类型: 0x%1")
                        .arg(QString::number(cmdType, 16)));
                }
                break;
        }
    });
}

void CallerManager::responseLogin(CallerProtocol* protocol)
{
    QByteArray data;
    data.resize(6);
    data[0] = 0x50;
    data[1] = 0x00;
    data[2] = rand();
    data[3] = rand();
    data[4] = rand();
    data[5] = rand();

    protocol->writeTcpData(data);

    if (m_logger) {
        m_logger->debug("呼叫器登录响应");
    }
}

void CallerManager::responseAuthentication(QByteArray& receiveData, CallerProtocol* protocol)
{
    if (receiveData.size() < 20) return;

    QString asciiCaller = QString(receiveData.mid(1, 6));
    uint32_t callerNum = receiveData.mid(16, 4).toHex().toUInt(nullptr, 16);

    QByteArray data;
    data.resize(2);
    data[0] = 0x51;
    data[1] = 0x00;

    protocol->writeTcpData(data);

    if (m_logger) {
        m_logger->debug(QString("呼叫器认证响应: 编号=%1").arg(callerNum));
    }

    if (markCallerOnline(callerNum, protocol)) {
        notifyRuntimeChanged();
    }
}

void CallerManager::responseButtonPressed(QByteArray& receiveData, CallerProtocol* protocol)
{
    if (receiveData.size() < 9) return;

    uint8_t taskType = receiveData.at(7);

    // 心跳包
    if (taskType == 0xff) {
        return;
    }

    // 发送间隔小于2秒，防止重复触发
    if (protocol->datetime.secsTo(QDateTime::currentDateTime()) < 2) {
        protocol->datetime = QDateTime::currentDateTime();
        return;
    }
    protocol->datetime = QDateTime::currentDateTime();

    uint32_t callerNum = receiveData.mid(3, 4).toHex().toUInt(nullptr, 16);
    uint8_t randNum = receiveData.at(8);

    if (m_callerNumToTaskName.contains(callerNum)) {
        bool runtimeChanged = markCallerOnline(callerNum, protocol);
        auto& info = ensureRuntimeInfo(callerNum);
        info.lastTriggerTime = QDateTime::currentDateTime();
        runtimeChanged = true;

        CallerMessage msg = {
            .taskManagerHandle = true,
            .taskName = m_callerNumToTaskName[callerNum],
            .randNum = randNum,
            .protocol = protocol
        };

        if (m_logger) {
            m_logger->info(QString("呼叫器按钮按下: 编号=%1, 任务=%2").arg(callerNum).arg(msg.taskName));
        }

        emit buttonPressed(msg);

        if (runtimeChanged) {
            notifyRuntimeChanged();
        }
    } else {
        if (m_logger) {
            m_logger->warning(QString("没有对应的呼叫器任务: 编号=%1").arg(callerNum));
        }
    }
}

void CallerManager::sendTaskResult(const CallerMessage& callerMsg, bool success)
{
    if (!callerMsg.protocol) {
        return;
    }
    
    // 下面的呼叫器无法解析指令失败的问题，导致一直返回成功。如果失败就不回复。
    if(!success){
        return;
    }
    
    QByteArray data;
    data.resize(3);
    data[0] = 0x70;
    data[1] = success ? 0x00 : 0x01;  // 00成功，非0表示失败
    data[2] = callerMsg.randNum;

    callerMsg.protocol->writeTcpData(data);
}

QList<CallerRuntimeInfo> CallerManager::getCallerRuntimeInfos() const
{
    QList<CallerRuntimeInfo> infos = m_runtimeInfos.values();
    std::sort(infos.begin(), infos.end(), [](const CallerRuntimeInfo& left, const CallerRuntimeInfo& right) {
        return left.callerNum < right.callerNum;
    });
    return infos;
}

bool CallerManager::markCallerOnline(int callerNum, CallerProtocol* protocol)
{
    auto& info = ensureRuntimeInfo(callerNum);
    const bool changed = !info.isOnline;
    info.isOnline = true;

    if (protocol) {
        m_protocolToCaller.insert(protocol, callerNum);
        if (auto client = protocol->getTcpClient()) {
            m_clientToProtocol.insert(client, protocol);
        }
    }

    return changed;
}

bool CallerManager::markCallerOffline(CallerProtocol* protocol)
{
    if (!protocol) {
        return false;
    }

    const int callerNum = m_protocolToCaller.take(protocol);
    if (callerNum <= 0) {
        return false;
    }

    auto it = m_runtimeInfos.find(callerNum);
    if (it == m_runtimeInfos.end()) {
        return false;
    }

    if (!it->isOnline) {
        return false;
    }

    it->isOnline = false;
    return true;
}

CallerRuntimeInfo& CallerManager::ensureRuntimeInfo(int callerNum)
{
    auto it = m_runtimeInfos.find(callerNum);
    if (it == m_runtimeInfos.end()) {
        CallerRuntimeInfo info;
        info.callerNum = callerNum;
        info.taskName = m_callerNumToTaskName.value(callerNum);
        it = m_runtimeInfos.insert(callerNum, info);
    } else {
        it->callerNum = callerNum;
        if (it->taskName.isEmpty()) {
            it->taskName = m_callerNumToTaskName.value(callerNum, it->taskName);
        }
    }

    return it.value();
}

void CallerManager::notifyRuntimeChanged()
{
    emit callerRuntimeInfoChanged();
}

void CallerManager::slot_disconnected()
{
    auto client = qobject_cast<infrastructure::io::TcpSocketClient*>(sender());
    CallerProtocol* protocol = nullptr;
    if (client) {
        protocol = m_clientToProtocol.take(client);
    }

    const bool runtimeChanged = markCallerOffline(protocol);
    if (protocol) {
        protocol->deleteLater();
    }

    if (runtimeChanged) {
        notifyRuntimeChanged();
    }

    if (m_logger) {
        m_logger->info("呼叫器断开连接");
    }
}

} // namespace caller
} // namespace application
